package com.autumn.platform.core;

import java.io.Closeable;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

import org.springframework.util.ClassUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public abstract class Utils {

    private Utils() {};

    /**
     * 
     */
    private static final Random RANDOM = new Random();

    private static Gson gson;

    /**
     * 当前主机IP
     */
    public static final String LOCALE_MAC = Inner.getMac();

    /**
     * 当前主机IP
     */
    public static final String LOCALE_HOST = Inner.getIp();

    /**
     * 当前操作系统
     */
    public static final String LOCALE_OS = Inner.getOS();

    /**
     * 当前进程
     */
    public static final String PID = Inner.getProcessId();

    /**
     * 字符串是否为空
     * 
     * @param cs 字符串
     * @return 是否为空白字符串
     */
    public static boolean isBlank(CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * 格式化字符串，去掉首尾空白字符，压缩中间连续的多个空白字符为一个空格
     * 
     * @param src 原始字符串
     * @return 压缩后的字符串
     */
    public static String formatWhitespace(String src) {
        if (null == src) {
            return null;
        }
        StringTokenizer st = new StringTokenizer(src);
        StringBuilder builder = new StringBuilder();
        while (st.hasMoreTokens()) {
            builder.append(st.nextToken()).append(" ");
        }
        return builder.toString();
    }

    /**
     * 将下划线格式的字段转为驼峰式，多个连续下划线作为一个处理
     * 
     * @param str 原始字符串
     * @return 驼峰式字符串
     */
    public static String convertToCamel(String str) {
        if (null != str) {
            StringBuilder sb = new StringBuilder();
            boolean upper = false, first = true;
            for (char ch : str.trim().toCharArray()) {
                if (ch == '-' || ch == '_') {
                    upper = !first;
                } else {
                    sb.append(upper ? Character.toUpperCase(ch) : Character.toLowerCase(ch));
                    upper = false;
                    first = false;
                }
            }
            return sb.toString();
        }
        return null;
    }

    /**
     * 将驼峰式转换为下划线
     * 
     * @param src
     * @return
     */
    public static String convertUnderscores(String src) {
        if (null != src) {
            StringBuilder sb = new StringBuilder(src.replace('.', '_'));
            for (int i = 1; i < sb.length() - 1; i++) {
                if (Character.isLowerCase(sb.charAt(i - 1)) && Character.isUpperCase(sb.charAt(i)) && Character.isLowerCase(sb.charAt(i + 1))) {
                    sb.insert(i++, '_');
                }
            }
            return sb.toString().toUpperCase();
        }
        return null;
    }

    /**
     * 字符串分割
     * 
     * @param src 要分割的字符串
     * @param separator 分隔符
     * @return 分割后的字符串列表
     */
    public static List<String> splitToList(String src, String separator) {
        List<String> l = new ArrayList<String>();
        if (null == src) {
            return null;
        } else if (null == separator) {
            l.add(src);
        } else {
            for (int i = src.indexOf(separator), o = separator.length(); i != -1; i = src.indexOf(separator)) {
                l.add(src.substring(0, i));
                src = src.substring(i + o);
            }
            if (!isBlank(src)) {
                l.add(src);
            }
        }
        return l;
    }

    /**
     * 字符串分割成固定长度的List，如果不够长度，使用默认字符串填充，如果长度超过指定长度，也不截断
     * 
     * @param src 要分割的字符串
     * @param separator 分隔符
     * @param minSize 返回集合的最小长度
     * @param defaultString 填充的默认字符串
     * @return 分割后的字符串列表
     */
    public static List<String> splitToList(String src, String separator, int minSize, String defaultString) {
        List<String> l = splitToList(src, separator);
        if (null == l) {
            l = new ArrayList<String>();
        }
        for (int i = minSize - l.size() - 1; i >= 0; i--) {
            l.add(defaultString);
        }
        return l;
    }

    /**
     * 用连接符连接列表中的项
     * 
     * @param list 对象列表
     * @param separator 连接符
     * @return 连接后的字符串
     */
    public static String join(List<?> list, String separator) {
        if (null == list || list.isEmpty()) {
            return null;
        }
        if (null == separator) {
            separator = ",";
        }
        StringBuilder sb = new StringBuilder();
        for (Object obj : list) {
            if (null != obj) {
                sb.append(separator).append(obj.toString());
            }
        }
        return sb.substring(separator.length()).toString();
    }

    /**
     * 用连接符连接数组中的项
     * 
     * @param arr 对象数组
     * @param separator 连接符
     * @return 连接后的字符串
     */
    public static String join(Object[] arr, String separator) {
        if (null == arr || arr.length == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (Object obj : arr) {
            if (null != obj) {
                sb.append(separator).append(obj.toString());
            }
        }
        return sb.substring(separator.length()).toString();
    }

    /**
     * 字符串转换为布尔类型
     * <p>
     * 转换为true的字符串有（不区分大小写）
     * <ul>
     * <li>"1"
     * <li>"Y"
     * <li>"TRUE"
     * <li>"ON"
     * <ul>
     * <p>
     * 
     * @param str 字符串
     * @return 布尔值
     */
    public static boolean string2Boolean(String str) {
        if (null == str) {
            return false;
        }
        str = str.trim();
        return "1".equals(str) || "Y".equalsIgnoreCase(str) || "TRUE".equalsIgnoreCase(str) || "ON".equalsIgnoreCase(str);
    }

    /**
     * 转换为List类型，如果不为空，只校验第一个元素的类型是否匹配
     * 
     * @param source 原始对象
     * @param elementType 元素类型
     * @return 转换后的集合
     */
    @SuppressWarnings("unchecked")
    public static <E> List<E> convertToList(Object source, Class<E> elementType) {
        if (null == source) {
            return null;
        } else if (source.getClass().isArray()) {
            List<E> list = new ArrayList<E>();
            Class<?> c = source.getClass();
            if (c.equals(int[].class)) {
                int[] arr = (int[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Integer.class)) {
                    for (Integer a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is int, but the target type is " + elementType);
                }
            } else if (c.equals(short[].class)) {
                short[] arr = (short[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Short.class)) {
                    for (Short a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is short, but the target type is " + elementType);
                }
            } else if (c.equals(long[].class)) {
                long[] arr = (long[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Long.class)) {
                    for (Long a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is long, but the target type is " + elementType);
                }
            } else if (c.equals(byte[].class)) {
                byte[] arr = (byte[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Byte.class)) {
                    for (Byte a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is byte, but the target type is " + elementType);
                }
            } else if (c.equals(boolean[].class)) {
                boolean[] arr = (boolean[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Boolean.class)) {
                    for (Boolean a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is boolean, but the target type is " + elementType);
                }
            } else if (c.equals(char[].class)) {
                char[] arr = (char[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Character.class)) {
                    for (Character a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is char, but the target type is " + elementType);
                }
            } else if (c.equals(float[].class)) {
                float[] arr = (float[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Float.class)) {
                    for (Float a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is float, but the target type is " + elementType);
                }
            } else if (c.equals(double[].class)) {
                double[] arr = (double[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else if (elementType.isAssignableFrom(Double.class)) {
                    for (Double a : arr) {
                        list.add((E) a);
                    }
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is double, but the target type is " + elementType);
                }
            } else {
                Object[] arr = (Object[]) source;
                if (arr.length == 0) {
                    return Collections.<E>emptyList();
                } else {
                    Object first = arr[0];
                    if (elementType.isAssignableFrom(first.getClass())) {
                        for (Object a : arr) {
                            list.add((E) a);
                        }
                        return list;
                    } else {
                        throw new ClassCastException("can not convert the list, the element type is " + first.getClass() + ", but the target type is " + elementType);
                    }
                }
            }
        } else if (source instanceof List) {
            List<E> list = (List<E>) source;
            if (list.isEmpty()) {
                return Collections.<E>emptyList();
            } else {
                Object first = list.get(0);
                if (elementType.isAssignableFrom(first.getClass())) {
                    return list;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is " + first.getClass() + ", but the target type is " + elementType);
                }
            }
        } else if (source instanceof Enumeration) {
            Enumeration<E> i = (Enumeration<E>) source;
            if (!i.hasMoreElements()) {
                return Collections.<E>emptyList();
            } else {
                Object first = i.nextElement();
                if (elementType.isAssignableFrom(first.getClass())) {
                    List<E> rs = new ArrayList<E>();
                    rs.add((E) first);
                    while (i.hasMoreElements()) {
                        rs.add(i.nextElement());
                    }
                    return rs;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is " + first.getClass() + ", but the target type is " + elementType);
                }
            }
        } else if (source instanceof Iterator) {
            Iterator<E> i = (Iterator<E>) source;
            if (!i.hasNext()) {
                return Collections.<E>emptyList();
            } else {
                Object first = i.next();
                if (elementType.isAssignableFrom(first.getClass())) {
                    List<E> rs = new ArrayList<E>();
                    rs.add((E) first);
                    while (i.hasNext()) {
                        rs.add(i.next());
                    }
                    return rs;
                } else {
                    throw new ClassCastException("can not convert the list, the element type is " + first.getClass() + ", but the target type is " + elementType);
                }
            }
        } else if (source instanceof Iterable) {
            return convertToList(((Iterable<?>) source).iterator(), elementType);
        } else if (source instanceof Map) {
            return convertToList(((Map<?, ?>) source).values(), elementType);
        } else {
            return convertToList(new Object[] {source}, elementType);
        }
    }

    /**
     * 转换IP地址
     * 
     * @param ip
     * @return
     */
    public static String convertIp(String ip) {
        if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || "0.0.0.0".equals(ip) || "localhost".equals(ip)) {
            return LOCALE_HOST;
        } else {
            return ip;
        }
    }

    /**
     * 关闭对象
     * 
     * @param closeable
     */
    public static void closeQuietly(Closeable... closeable) {
        if (null != closeable && closeable.length >= 1) {
            for (Closeable c : closeable) {
                if (null != c) {
                    try {
                        c.close();
                    } catch (IOException i) {
                    }
                }
            }
        }
    }

    /**
     * 日期时间格式化
     * 
     * @param date 日期对象
     * @param format 日期格式
     * @return 格式化后的日期字符串
     */
    public static String getFormatDate(Date date, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        String rs = df.format(date);
        return rs;
    }

    /**
     * 日期计算
     * 
     * @param date 日期字符串
     * @param format 日期格式
     * @param mYear 需要增加的年数，如果需要减少，传入负数
     * @param mMonth 需要增加的月数，如果需要减少，传入负数
     * @param mDay 需要增加的日数，如果需要减少，传入负数
     * @return 计算后并且格式化的日期字符串
     * @throws ParseException
     */
    public static String dateCalculate(String date, String format, int mYear, int mMonth, int mDay) throws ParseException {
        DateFormat fo = new SimpleDateFormat(format);
        Date sDate = fo.parse(date);
        Date rDate = dateCalculate(sDate, mYear, mMonth, mDay);
        return fo.format(rDate);
    }

    /**
     * 日期计算
     * 
     * @param date 日期对象
     * @param mYear 需要增加的年数，如果需要减少，传入负数
     * @param mMonth 需要增加的月数，如果需要减少，传入负数
     * @param mDay 需要增加的日数，如果需要减少，传入负数
     * @return 计算后的日期对象
     */
    public static Date dateCalculate(Date date, int mYear, int mMonth, int mDay) {
        Calendar tempCal = Calendar.getInstance();
        tempCal.setTime(date);
        tempCal.add(Calendar.YEAR, mYear);
        tempCal.add(Calendar.MONTH, mMonth);
        tempCal.add(Calendar.DATE, mDay);
        return tempCal.getTime();
    }

    /**
     * 根据日期格式获取指定时间的毫秒数
     * 
     * @param datetime 日期时间
     * @param datetimeFormat 日期时间格式
     * @return 毫秒数
     * @throws ParseException
     */
    public static long str2longDate(String datetime, String datetimeFormat) throws ParseException {
        DateFormat fo = new SimpleDateFormat(datetimeFormat);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fo.parse(datetime));
        return calendar.getTimeInMillis();
    }

    /**
     * 根据毫秒数返回字符串格式的日期
     * 
     * @param datetime 毫秒数
     * @param datetimeFormat 日期时间格式
     * @return 字符串格式的日期
     */
    public static String long2strData(long datetime, String datetimeFormat) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(datetime);
        return Utils.getFormatDate(calendar.getTime(), datetimeFormat);
    }

    /**
     * 产生随机字母
     * 
     * @param count
     * @return
     */
    public static String randomAlphabetic(final int count) {
        return random(count, 0, 0, true, false, null, RANDOM, null);
    }

    /**
     * 产生随机字母和数字
     * 
     * @param count
     * @return
     */
    public static String randomAlphanumeric(final int count) {
        return random(count, 0, 0, true, true, null, RANDOM, null);
    }

    /**
     * 产生随机数字
     * 
     * @param count
     * @return
     */
    public static String randomNumeric(final int count) {
        return random(count, 0, 0, false, true, null, RANDOM, null);
    }

    /**
     * 产生随机字母
     * 
     * @param count
     * @param dateFormat
     * @return
     */
    public static String randomAlphabetic(final int count, final String dateFormat) {
        return random(count, 0, 0, true, false, null, RANDOM, dateFormat);
    }

    /**
     * 产生随机字母和数字
     * 
     * @param count
     * @param dateFormat
     * @return
     */
    public static String randomAlphanumeric(final int count, final String dateFormat) {
        return random(count, 0, 0, true, true, null, RANDOM, dateFormat);
    }

    /**
     * 产生随机数字
     * 
     * @param count
     * @param dateFormat
     * @return
     */
    public static String randomNumeric(final int count, final String dateFormat) {
        return random(count, 0, 0, false, true, null, RANDOM, dateFormat);
    }

    /**
     * 产生随机字符串
     * 
     * @param count 随机字符串部分长度
     * @param chars 有效字符
     * @param dateFormat 日期部分的格式，为null时不包含日期
     * @return 随机字符串
     */
    public static String random(int count, final String chars, String dateFormat) {
        return random(count, 0, 0, false, false, chars.toCharArray(), RANDOM, dateFormat);
    }

    /**
     * 产生随机字符串
     * 
     * @param count 随机字符串部分长度
     * @param start 产生随机整数时的最小值
     * @param end 产生随机整数时的最大值
     * @param letters 是否只包含字母
     * @param numbers 是否只包含数字
     * @param chars 有效字符
     * @param random 随机工具类
     * @param dateFormat 日期部分的格式，为null时不包含日期
     * @return 随机字符串
     */
    public static String random(int count, int start, int end, final boolean letters, final boolean numbers, final char[] chars, final Random random, String dateFormat) {
        if (count == 0) {
            return "";
        } else if (count < 0) {
            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
        }
        if (chars != null && chars.length == 0) {
            throw new IllegalArgumentException("The chars array must not be empty");
        }

        if (start == 0 && end == 0) {
            if (chars != null) {
                end = chars.length;
            } else {
                if (!letters && !numbers) {
                    end = Integer.MAX_VALUE;
                } else {
                    end = 'z' + 1;
                    start = ' ';
                }
            }
        } else {
            if (end <= start) {
                throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")");
            }
        }

        final char[] buffer = new char[count];
        final int gap = end - start;

        while (count-- != 0) {
            char ch;
            if (chars == null) {
                ch = (char) (random.nextInt(gap) + start);
            } else {
                ch = chars[random.nextInt(gap) + start];
            }
            if (letters && Character.isLetter(ch) || numbers && Character.isDigit(ch) || !letters && !numbers) {
                if (ch >= 56320 && ch <= 57343) {
                    if (count == 0) {
                        count++;
                    } else {
                        // low surrogate, insert high surrogate after putting it in
                        buffer[count] = ch;
                        count--;
                        buffer[count] = (char) (55296 + random.nextInt(128));
                    }
                } else if (ch >= 55296 && ch <= 56191) {
                    if (count == 0) {
                        count++;
                    } else {
                        // high surrogate, insert low surrogate before putting it in
                        buffer[count] = (char) (56320 + random.nextInt(128));
                        count--;
                        buffer[count] = ch;
                    }
                } else if (ch >= 56192 && ch <= 56319) {
                    // private high surrogate, no effing clue, so skip it
                    count++;
                } else {
                    buffer[count] = ch;
                }
            } else {
                count++;
            }
        }
        String rs = new String(buffer);
        if (null != dateFormat) {
            SimpleDateFormat df = new SimpleDateFormat(dateFormat);
            rs = df.format(new Date()) + rs;
        }
        return rs;
    }

    /**
     * 对象转换为JSON字符串
     * 
     * @param src
     * @return
     */
    public static String toJson(Object src) {
        if (null == src) {
            return null;
        } else if (src instanceof String) {
            return (String) src;
        } else if (ClassUtils.isPrimitiveWrapper(src.getClass())) {
            return src.toString();
        } else {
            Gson gson = getGson();
            return gson.toJson(src);
        }
    }

    /**
     * JSON字符串转换为对象
     * 
     * @param data
     * @param cls
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <E> E formJson(String data, Class<E> cls) {
        if (isBlank(data)) {
            return null;
        } else if (String.class.isAssignableFrom(cls)) {
            return cls.cast(data);
        } else if (int.class.equals(cls) || Integer.class.equals(cls)) {
            return (E) Integer.valueOf(data);
        } else if (long.class.equals(cls) || Long.class.equals(cls)) {
            return (E) Long.valueOf(data);
        } else if (double.class.equals(cls) || Double.class.equals(cls)) {
            return (E) Double.valueOf(data);
        } else {
            Gson gson = getGson();
            E rs = gson.fromJson(data, cls);
            return rs;
        }
    }

    private static Gson getGson() {
        if (null == gson) {
            synchronized (Utils.class) {
                if (null == gson) {
                    GsonBuilder gsonBuilder = new GsonBuilder();
                    Gson gson = gsonBuilder.create();
                    Utils.gson = gson;
                }
            }
        }
        return gson;
    }

    private static class Inner {

        private static String getOS() {
            return System.getProperty("os.name");
        }

        private static String getProcessId() {
            try {
                RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
                String info = runtime.getName();
                int index = info.indexOf("@");
                if (index != -1) {
                    return info.substring(0, index);
                }
            } catch (Exception e) {
            }
            return null;
        }

        private static String getMac() {
            try {
                //获取网卡，获取地址
                InetAddress host = Inet4Address.getLocalHost();
                byte[] mac = NetworkInterface.getByInetAddress(host).getHardwareAddress();
                StringBuffer sb = new StringBuffer("");
                for (int i = 0; i < mac.length; i++) {
                    if (i != 0) {
                        sb.append("-");
                    }
                    //字节转换为整数
                    int temp = mac[i] & 0xff;
                    String str = Integer.toHexString(temp);
                    if (str.length() == 1) {
                        sb.append("0" + str);
                    } else {
                        sb.append(str);
                    }
                }
                return sb.toString().toUpperCase();
            } catch (Exception e) {
                return null;
            }
        }

        private static String getIp() {
            try {
                InetAddress host = Inet4Address.getLocalHost();
                return host.getHostAddress();
            } catch (Exception e) {
                return null;
            }
        }
    }
}
